/* * Author: Chris Seguin * * This software has been developed under the copyleft * rules of the GNU General Public License. Please * consult the GNU General Public License for more * details about use and distribution of this software. */ package org.acm.seguin.refactor.method; import java.util.Iterator; import org.acm.seguin.parser.ast.SimpleNode; import org.acm.seguin.refactor.ComplexTransform; import org.acm.seguin.refactor.RefactoringException; import org.acm.seguin.summary.FieldAccessSummary; import org.acm.seguin.summary.FieldSummary; import org.acm.seguin.summary.FileSummary; import org.acm.seguin.summary.MessageSendSummary; import org.acm.seguin.summary.MethodSummary; import org.acm.seguin.summary.Summary; import org.acm.seguin.summary.TypeDeclSummary; import org.acm.seguin.summary.TypeSummary; import org.acm.seguin.summary.VariableSummary; import org.acm.seguin.summary.query.FieldQuery; import org.acm.seguin.summary.query.GetTypeSummary; import org.acm.seguin.summary.query.MethodQuery; import org.acm.seguin.summary.query.SamePackage; /** * Moves a method from one class to another. Generally used to move a method * into a local variable or a parameter. * *@author Chris Seguin */ public class MoveMethodRefactoring extends MethodRefactoring { private MethodSummary methodSummary; private TypeSummary typeSummary; private Summary destination; /** * Constructor for the MoveMethodRefactoring object */ protected MoveMethodRefactoring() { } /** * Sets the Method attribute of the MoveMethodRefactoring object * *@param value The new Method value */ public void setMethod(MethodSummary value) { methodSummary = value; Summary current = methodSummary; while (!(current instanceof TypeSummary)) { current = current.getParent(); } typeSummary = (TypeSummary) current; } /** * Sets the Destination attribute of the MoveMethodRefactoring object * *@param value The new Destination value */ public void setDestination(Summary value) { destination = value; } /** * Gets the description of the refactoring * *@return the description */ public String getDescription() { return "Moving " + methodSummary.toString() + " from " + typeSummary.toString() + " to " + destination.toString(); } /** * Gets the ID attribute of the MoveMethodRefactoring object * *@return The ID value */ public int getID() { return MOVE_METHOD; } /** * Describes the preconditions that must be true for this refactoring to be * applied * *@exception RefactoringException thrown if one or more of the * preconditions is not satisfied. The text of the exception provides a * hint of what went wrong. */ protected void preconditions() throws RefactoringException { Iterator iter = methodSummary.getDependencies(); while ((iter != null) && (iter.hasNext())) { Summary next = (Summary) iter.next(); // Check to see if we have any private fields without the appropriate getters/setters if (next instanceof FieldAccessSummary) { FieldAccessSummary fas = (FieldAccessSummary) next; checkFieldAccess(fas); } else if (next instanceof MessageSendSummary) { MessageSendSummary mss = (MessageSendSummary) next; checkMessageSend(mss); } } if (destination instanceof VariableSummary) { VariableSummary varSummary = (VariableSummary) destination; TypeDeclSummary typeDecl = varSummary.getTypeDecl(); TypeSummary destType = GetTypeSummary.query(typeDecl); if (destType == null) { throw new RefactoringException("The parameter " + varSummary.getName() + " is a primitive"); } FileSummary fileSummary = (FileSummary) destType.getParent(); if (fileSummary.getFile() == null) { throw new RefactoringException("The source code for " + destType.getName() + " is not modifiable"); } } } /** * Performs the transform on the rest of the classes */ protected void transform() { ComplexTransform transform = getComplexTransform(); // Update the method declaration to have the proper permissions SimpleNode methodDecl = removeMethod(typeSummary, transform); if (methodDecl == null) { return; } update(methodDecl); TypeSummary destType; if (destination instanceof VariableSummary) { VariableSummary varSummary = (VariableSummary) destination; TypeDeclSummary typeDecl = varSummary.getTypeDecl(); destType = GetTypeSummary.query(typeDecl); } else if (destination instanceof TypeSummary) { destType = (TypeSummary) destination; } else { return; } addMethodToDest(transform, methodDecl, destType); } /** * Removes the method from the source * *@param source the source type *@param transform the transform *@return Description of the Returned Value */ protected SimpleNode removeMethod(TypeSummary source, ComplexTransform transform) { RemoveMethodTransform rft = new RemoveMethodTransform(methodSummary); transform.add(rft); InvokeMovedMethodTransform immt = new InvokeMovedMethodTransform(methodSummary, destination); transform.add(immt); FileSummary fileSummary = (FileSummary) source.getParent(); transform.apply(fileSummary.getFile(), fileSummary.getFile()); return rft.getMethodDeclaration(); } /** * Adds the method to the destination class * *@param transform The feature to be added to the MethodToDest attribute *@param methodDecl The feature to be added to the MethodToDest attribute *@param dest The feature to be added to the MethodToDest attribute */ protected void addMethodToDest(ComplexTransform transform, SimpleNode methodDecl, TypeSummary dest) { transform.clear(); AddMethodTransform aft = new AddMethodTransform(methodDecl); transform.add(aft); AddMethodTypeVisitor visitor = new AddMethodTypeVisitor(); methodSummary.accept(visitor, transform); // Add appropriate import statements - to be determined later FileSummary parentFileSummary = (FileSummary) dest.getParent(); transform.apply(parentFileSummary.getFile(), parentFileSummary.getFile()); } /** * Gets the name of the getter for the field * *@param summary the field summary *@return the getter */ private String getFieldGetter(FieldSummary summary) { String typeName = summary.getType(); String prefix = "get"; if (typeName.equalsIgnoreCase("boolean")) { prefix = "is"; } String name = summary.getName(); return prefix + name.substring(0, 1).toUpperCase() + name.substring(1); } /** * Gets the name of the setter for the field * *@param summary the field summary *@return the setter */ private String getFieldSetter(FieldSummary summary) { String prefix = "set"; String name = summary.getName(); return prefix + name.substring(0, 1).toUpperCase() + name.substring(1); } /** * Checks if we can properly transform the field access * *@param fas Description of Parameter *@exception RefactoringException Description of Exception */ private void checkFieldAccess(FieldAccessSummary fas) throws RefactoringException { if ((fas.getPackageName() == null) && ((fas.getObjectName() == null) || fas.getObjectName().equals("this"))) { // Now we have to find the field FieldSummary field = FieldQuery.find(typeSummary, fas.getFieldName()); if (field != null) { if (field.getModifiers().isPrivate()) { checkForMethod(fas, field); } } } } /** * For a private field, check that we have the correct setters or getters * (as appropriate) * *@param fas Description of Parameter *@param field Description of Parameter *@exception RefactoringException Description of Exception */ private void checkForMethod(FieldAccessSummary fas, FieldSummary field) throws RefactoringException { String methodName; if (fas.isAssignment()) { methodName = getFieldSetter(field); } else { methodName = getFieldGetter(field); } MethodSummary method = MethodQuery.find(typeSummary, methodName); if (method == null) { throw new RefactoringException("Unable to find the appropriate method (" + methodName + ") for private field access in " + typeSummary.getName()); } } /** * Updates the node fore move method * *@param node Description of Parameter */ private void update(SimpleNode node) { MoveMethodVisitor mmv = new MoveMethodVisitor(typeSummary, methodSummary, destination); node.jjtAccept(mmv, null); } /** * Description of the Method * *@param mss Description of Parameter *@exception RefactoringException Description of Exception */ private void checkMessageSend(MessageSendSummary mss) throws RefactoringException { if ((mss.getPackageName() == null) && ((mss.getObjectName() == null) || mss.getObjectName().equals("this"))) { MethodSummary method = MethodQuery.find(typeSummary, mss.getMessageName()); if (method == null) { throw new RefactoringException("Unable to find the method (" + mss.getMessageName() + ") in " + typeSummary.getName()); } if (method.getModifiers().isPrivate()) { throw new RefactoringException("Moving a method (" + mss.getMessageName() + ") from " + typeSummary.getName() + " that requires private access is illegal"); } if (method.getModifiers().isPackage()) { TypeSummary destType; if (destination instanceof VariableSummary) { VariableSummary varSummary = (VariableSummary) destination; TypeDeclSummary typeDecl = varSummary.getTypeDecl(); destType = GetTypeSummary.query(typeDecl); } else if (destination instanceof TypeSummary) { destType = (TypeSummary) destination; } else { throw new RefactoringException("Cannot find the type associated with " + destination.getName()); } if (!SamePackage.query(typeSummary, destType)) { throw new RefactoringException("Moving a method (" + mss.getMessageName() + ") from " + typeSummary.getName() + " to a different package that requires package access is illegal."); } } } } }